<?php

/**
 * ListController
 *
 * @author yukio
 * @version $Id$
 */

require_once 'Zend/Controller/Action.php';
require_once 'Zend/Exception.php';
require_once 'Okute/Exception.php';
require_once 'Okute/Util/Data.php';

require_once APPLICATION_PATH . '/models/RequestModel.php';
require_once APPLICATION_PATH . '/models/RequesterModel.php';
require_once APPLICATION_PATH . '/models/ShelterModel.php';
require_once APPLICATION_PATH . '/models/SupportModel.php';
require_once APPLICATION_PATH . '/models/CommentModel.php';
require_once APPLICATION_PATH . '/models/StatusModel.php';
require_once APPLICATION_PATH . '/models/AclModel.php';
require_once APPLICATION_PATH . '/models/AccountModel.php';

class ListController extends Zend_Controller_Action
{
    /**
     * アプリケーション設定
     * @var Zend_Config
     */
    private $_config;

    /**
     * アクセスコントロール
     * @var Zend_Acl
     */
    private $_acl;
    
    /**
     * アカウントモデル
     * @var AccountModel
     */
    private $_accountModel;
    
    /**
     * 共通関数用
     * @var Okute_Util_Data
     */
    private $_util;

    /**
     * リダイレクトヘルパー
     * @var Okute_Controller_Action_Helper_Redirector
     */
    private $_redirector;
    
    /**
     * Listコントローラの初期化を行う。このコントローラを実行する実行者のロールによって
     * 1.データの更新者
     * 2.view内のリンク
     * が異なるため、それぞれ設定を行う。
     *
     * @return void
     * @throws Okute_Exception
     */
    public function init()
    {
        $this->_session = new Zend_Session_Namespace('okute');
        
        if ($this->_session->role === null) {
            throw new Okute_Exception('Cannot determine user role');
        }
        
        // ビューの設定
        $this->view->base = $this->getRequest()->getBaseUrl();
        $this->_accountModel = new AccountModel();
        $this->view->user = $this->_accountModel->getUserName();
        $this->view->role = $this->_session->role;
        
        if ($this->_session->role === AclModel::ROLE_REQUESTER) {
            $this->view->urlMenu = $this->view->base . '/request';
            $this->view->show_edit_link = true;
            $this->view->show_status_form = true;
            $this->view->show_enable = false;
        }
        if ($this->_session->role === AclModel::ROLE_SUPPORTER ||
            $this->_session->role === AclModel::ROLE_CARRIER) {
            $this->view->urlMenu = $this->view->base . '/support';
            $this->view->show_edit_link = false;
            $this->view->show_status_form = true;
            $this->view->show_enable = false;
        }
        if ($this->_session->role === AclModel::ROLE_OPERATOR) {
            $this->view->urlMenu = $this->view->base . '/operator';
            $this->view->show_edit_link = true;
            $this->view->show_status_form = true;
            $this->view->show_enable = true;
        }        
        
        $this->_util = new Okute_Util_Data($this->view);
        $this->_config = Zend_Registry::get('config');
        $this->_acl = Zend_Registry::get('acl');
        $this->_redirector = Zend_Registry::get('redirector');
    }

    /**
     * indexActionは要求物資の一覧を表示する。
     * 申請希望者は自分自身の申請のみ閲覧可能。
     * 団体は全ての申請を閲覧可能。団体でGET/POSTのパラメータとしてrequester_idが
     * 存在する場合には、そのrequester_idでフィルタする。
     *
     * @return void
     */
    public function indexAction()
    {
        if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_REQUEST_LIST)) {
            throw new Okute_Exception('Access Control Error');
        }

        $filterRequester = null;
        $filterStatus = null;
        
        if ($this->_session->role === AclModel::ROLE_REQUESTER) {
            $filterRequester = $this->_accountModel->getAccountId();
        } else {
            $filterRequester = $this->getRequest()->getParam('requester_id', null);
        }
        
        $this->_showRequestList($filterRequester, $filterStatus);
    }
    
    /**
     * unassignedActionは未割り当てリスト一覧表示を行う。
     * 申請希望者は自分自身の申請のみ閲覧可能。
     * 団体は全ての申請を閲覧可能。団体でGET/POSTのパラメータとしてrequester_idが
     * 存在する場合には、そのrequester_idでフィルタする。
     * 
     * @return void
     */
    public function unassignedAction()
    {
        if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_REQUEST_LIST)) {
            throw new Okute_Exception('Access Control Error');
        }
        
        $filterRequester = null;
        $filterStatus = StatusModel::REQUEST_WAIT;
        
        if ($this->_session->role === AclModel::ROLE_REQUESTER) {
            $filterRequester = $this->_accountModel->getAccountId();
        } else {
            $filterRequester = $this->getRequest()->getParam('requester_id', null);
        }
        
        $this->_showRequestList($filterRequester, $filterStatus);
    }    
    
    /**
     * detailActionは要請物資の詳細を表示する。
     *
     * @return void
     * @throws Okute_Exception
     */
    public function detailAction()
    {
        if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_REQUEST_LIST)) {
            throw new Okute_Exception('Access Control Error');
        }

        $format = $this->_session->format;
        $commentCount = $this->_config->display->comment_count_per_page->$format;
        $commentPage = $this->getRequest()->getParam('page',1);

        $map = $this->_session->requestMap;
        if ($map === null) {
            throw new Okute_Exception('Session data "requestMap" is required');
        }
        $displayId = $this->getRequest()->getParam('id', null);
        if ($displayId === null) {
            throw new Okute_Exception('GET parameter "displayId" is requred');
        }
        $reqId = $map[$displayId];
        if ($reqId === null) {
            throw new Okute_Exception('No valid "req_id" in "requestMap"');
        }

        // 請求インスタンスの取得
        $requestModel = new RequestModel();
        $data = $requestModel->fetchRow(
            $requestModel->select()->where('req_id = ?', $reqId));

        // 請求者インスタンスの取得
        $requesterModel = new RequesterModel();
        $requester = $requesterModel->fetchRow(
            $requesterModel->select()->where('requester_id = ?', $data->requester_id));

        // 避難所のインスタンス作成
        $shelterModel = new ShelterModel();
        $shelter = $shelterModel->fetchRow(
            $shelterModel->select()->where('shelter_id = ?',$data->addressee_shelter_id));

        // 支援インスタンス作成
        $supportModel = new SupportModel();
        $support = $supportModel->fetchRow(
            $supportModel->select()->where('req_id = ?', $reqId));

        // 支援状態インスタンス作成
        $statusModel = new StatusModel();
        $status = $statusModel->fetchRow(
            $statusModel->select()->where('req_id =?', $reqId)
                        ->order('update_datetime DESC')
                        ->limit(1));

        // コメントデータをページネーションで取得
        $commentModel = new CommentModel();
        $select = $commentModel->select();
        $select->where('req_id = ?', $reqId);
        $select->order('create_datetime');

        $comments = Zend_Paginator::factory($select);

        $comments->setCurrentPageNumber($commentPage);
        $comments->setItemCountPerPage($commentCount);

        $this->_session->reqId = $reqId;
        $this->_session->displayId = $displayId;
        $this->_session->commentPage = $comments->getCurrentPageNumber();
        $this->_util->dataToSession($data);

        $this->_util->dataToView($data);
        $this->view->requester = $requester;
        $this->view->shelter = $shelter;
        $this->view->comments = $comments;
        $this->view->status = $status;
        $this->view->support = $support;
        $this->view->page = $comments->getPages();
        $this->view->displayId = $displayId;

        $opts = array();
        if ($this->_acl->isAllowed($this->_session->role, AclModel::RES_STAT_WAIT)) {
            $opts[StatusModel::REQUEST_WAIT] = StatusModel::$display[StatusModel::REQUEST_WAIT];
        }
        if ($this->_acl->isAllowed($this->_session->role, AclModel::RES_STAT_SUPPORT)) {
            $opts[StatusModel::REQUEST_ASSIGNED] = StatusModel::$display[StatusModel::REQUEST_ASSIGNED];
        }
        if ($this->_acl->isAllowed($this->_session->role, AclModel::RES_STAT_DELIVER)) {
            $opts[StatusModel::REQUEST_DELIVERY] = StatusModel::$display[StatusModel::REQUEST_DELIVERY];
            $opts[StatusModel::REQUEST_COMPLETED] = StatusModel::$display[StatusModel::REQUEST_COMPLETED];
        }
        if ($this->_acl->isAllowed($this->_session->role, AclModel::RES_STAT_R_CANCEL)) {
            $opts[StatusModel::REQUEST_REQUESTER_CANCEL] = StatusModel::$display[StatusModel::REQUEST_REQUESTER_CANCEL];
        }
        if ($this->_acl->isAllowed($this->_session->role, AclModel::RES_STAT_S_CANCEL)) {
            $opts[StatusModel::REQUEST_SUPPORTER_CANCEL] = StatusModel::$display[StatusModel::REQUEST_SUPPORTER_CANCEL];
        }
        $this->view->status_opts = $opts;
        $this->view->status_opt = StatusModel::$display[$support->last_status];
    }
    
    /**
     * statusActionは要請物資のステータス変更を行う際に前処理を実施するアクションである。
     * 現在の仕様ではステータス変更は団体および管理者が実施できる。
     *
     * @return void
     * @throws Okute_Exception
     * @throws Exception
     */
    public function statusAction()
    {
        // アクセス権の確認
        $allowedResource = array(AclModel::RES_STAT_S_CANCEL,
                                 AclModel::RES_STAT_SUPPORT,
                                 AclModel::RES_STAT_DELIVER);
        if ($this->_acl->areAllowed($this->_session->role, $allowedResource)) {
            throw new Okute_Exception('Access Control Error');
        }

        if ($this->_session->reqId === null) {
            throw new Okute_Exception('Session data "reqId" is required');
        }
        if ($this->_session->displayId === null) {
            throw new Okute_Exception('Session data "displayId" is required');
        }

        $statusId = $this->getRequest()->getParam('status',-1);
        $this->_checkStatusAcl($statusId);

        $db = Zend_Registry::get('database', null);
        if ($db === null) {
            throw new Okute_Exception('Invalid registry "database" ');
        }
        $db->beginTransaction();
        try {
            $statusModel = new StatusModel();
            $statusModel->insert(array('req_id' => $this->_session->reqId,
                                       'update_datetime' => 'NOW()',
                                       'status' => $statusId,
                                       'update_id' => $this->_accountModel->getAccountId()));

            $supportModel = new SupportModel();
            $where = $supportModel->getAdapter()->quoteInto('req_id = ?', $this->_session->reqId);
            $supportModel->update(array('last_status' => $statusId), $where);
            $db->commit();
        } catch (Exception $e) {
            $db->rollBack();
            throw $e;
        }

        $this->_redirector->redirectWithSession(
        	$this->view->base . '/list/detail/id/' . $this->_session->displayId);
    }
    
    /**
     * フィルタされた申請リストを取得し表示する。
     * 
     * @param integer $filterRequester
     * @param integer $filterStatus
     * @return void
     */
    private function _showRequestList($filterRequester, $filterStatus)
    {
        $format = $this->_session->format;
        $count = $this->_config->display->request_count_per_page->$format;
        $page = $this->getRequest()->getParam('page',1);
        $order = $this->_getOrder($this->getRequest()->getParam('order'));
        
        // ステータスのフィルタ情報を記録
        if ($filterStatus === null) {
            $filterStatus = $this->getRequest()->getParam('status');
            if ($filterStatus === null && $this->_session->filterStatus !== null) {
                $filterStatus = $this->_session->filterStatus;
            }
        }
        $this->_session->filterStatus = $filterStatus;
        $this->view->filterStatus = $filterStatus;
        
        $showDisable = false;
        if ($this->_acl->isAllowed($this->_session->role, AclModel::RES_REQUEST_DELETE)) {
            $showDisable = true;
        }
        
        $requestModel = new RequestModel();
        $select = $requestModel->getSelect($filterRequester, $filterStatus, $order, $showDisable);
        
        Zend_Paginator::setDefaultScrollingStyle('Sliding');
        $supports = Zend_Paginator::factory($select);
        $supports->setCurrentPageNumber($page);
        $supports->setItemCountPerPage($count);

        // 不正なページが入力されたときの対策としてpaginatorが保持するページで上書きする
        $page = $supports->getCurrentPageNumber();

        $map = array();
        $requests = array();
        $offset = $count * ($page-1);
        $displayId = $offset + 1;

        foreach($supports as $support) {
            // 表示idとreq_idのマップを作成
            $map[$displayId] = $support->req_id;
            
            // ShelterModelを作成
            $shelterModel = new ShelterModel();
            $shelter = $shelterModel->createRow();
            foreach($shelter->toArray() as $key => $val) {
                if (isset($support->$key)) {
                    $shelter->$key = $support->$key;
                }
            }
            
            // Smartyの簡易表示項目を作成
            $requests[$displayId] = array(
                'req_id'       => $support->req_id,
                'requester_id' => $support->requester_id,
                'mail_address' => $support->mail_address,
                'date'         => $support->create_datetime,
                'name'         => $support->req_content,
                'count'        => $support->req_amount,
                'req_status'   => StatusModel::$display[$support->last_status],
                'num_comment'  => $support->request_note_count,
                'enable'       => $support->enable,
                'shelter'      => $shelter
            );
            $displayId++;
        }

        $this->_session->requestMap = $map;
        
        $this->view->requests = $requests;
        $this->view->count = $requestModel->getCountPerStatus($filterRequester);
        $this->view->page = $supports->getPages();
        $this->view->status_opts = StatusModel::$display;
        $this->view->status_opt = $filterStatus;
    }
    
    /**
     * ステータス変更に対するアクセス制御チェック
     * 
     * @param integer $statusId
     */
    private function _checkStatusAcl($statusId)
    {
        if ($statusId == StatusModel::REQUEST_WAIT) {
            if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_STAT_WAIT)) {
                throw new Okute_Exception('Access Control Error');
            }
        } else if ($statusId == StatusModel::REQUEST_ASSIGNED) {
            if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_STAT_SUPPORT)) {
                throw new Okute_Exception('Access Control Error');
            }
        } else if ($statusId == StatusModel::REQUEST_ASSIGNED ||
                   $statusId == StatusModel::REQUEST_DELIVERY ||
                   $statusId == StatusModel::REQUEST_COMPLETED) {
            if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_STAT_DELIVER)) {
                throw new Okute_Exception('Access Control Error');
            }
        } else if ($statusId == StatusModel::REQUEST_REQUESTER_CANCEL) {
            if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_STAT_R_CANCEL)) {
                throw new Okute_Exception('Access Control Error');
            }
        } else if ($statusId == StatusModel::REQUEST_SUPPORTER_CANCEL) {
            if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_STAT_S_CANCEL)) {
                throw new Okute_Exception('Access Control Error');
            }
        } else {
            throw new Okute_Exception('Invalid Status');
        }
    }

    /**
     * 表示順序を切り替えるためのフィルタ用SQL文字列を作成
     * 
     * @param integer $statusId
     */
    private function _getOrder($order)
    {
        $str = '';
        if ($order === null) {
            if ($this->_session->filterOrder === null) {
                $order = 'date_desc';
            } else {
                $order = $this->_session->filterOrder;
            }
        }

        switch ($order) {            
            case 'date_asc':
                $str = 'support_info.create_datetime ASC';
                break;
            case 'date_desc':
                $str = 'support_info.create_datetime DESC';
                break;
            case 'requester_asc':
                $str = 'request_info.requester_id ASC';
                break;
            case 'requester_desc':
                $str = 'request_info.requester_id DESC';
                break;
            case 'status_asc':
                $str = 'support_info.last_status ASC';
                break;
            case 'status_desc':
                $str = 'support_info.last_status DESC';
                break;
            case 'comment_count_desc':
                $str = 'request_info.request_note_count DESC';
                break;
            case 'comment_count_asc':
                $str = 'request_info.request_note_count ASC';
                break;
            case 'id_desc':
                $str = 'support_info.req_id DESC';
                break;
            case 'id_asc':
                $str = 'support_info.req_id ASC';
                break;
            case 'mail_desc':
                $str = 'mst_requester.mail_address DESC';
                break;
            case 'mail_asc':
                $str = 'mst_requester.mail_address ASC';
                break;
            case 'shelter_desc':
                $str = 'mst_shelter.sortkey DESC';
                break;
            case 'shelter_asc':
                $str = 'mst_shelter.sortkey ASC';
                break;
            default:
                $str = 'support_info.create_datetime';
                break;
        }
        $this->_session->filterOrder = $order;

        return $str;
    }
}